package com.tsd.interceptor;


import com.tsd.core.vo.PageBean;
import org.apache.ibatis.executor.parameter.ParameterHandler;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.reflection.DefaultReflectorFactory;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.Map;
import java.util.Properties;
import java.util.regex.Matcher;
import java.util.regex.Pattern;


/**
 * 分页拦截器
 *
 * @author
 */
@Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})})
public class PageInterceptor implements Interceptor {
    private static final Logger LOGGER = LoggerFactory.getLogger(PageInterceptor.class);

    /*
     * 拦截器要执行的方法
     */
    @Override
    @SuppressWarnings("unchecked")
    public Object intercept(Invocation invocation) throws Throwable {
        StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
        MetaObject metaObject = MetaObject.forObject(statementHandler, SystemMetaObject.DEFAULT_OBJECT_FACTORY, SystemMetaObject.DEFAULT_OBJECT_WRAPPER_FACTORY, new DefaultReflectorFactory());
        MappedStatement mappedStatement = (MappedStatement) metaObject.getValue("delegate.mappedStatement");
        String id = mappedStatement.getId();
        if (id.matches(".+ByPage$")) {
            BoundSql boundSql = statementHandler.getBoundSql();
            Map<String, Object> params = (Map<String, Object>) boundSql.getParameterObject();
            PageBean page = (PageBean) params.get("page");
            if (page == null) {
                return invocation.proceed();
            }
            String sql = boundSql.getSql();
            if (page.getNeedCalPageCount() == 1) {
                //默认是需要分页
                String countSql = "select count(1) from (" + removeOrders(sql) + ") as_page_count";
                // String countSql = this.getCountSql(sql);
                Connection connection = (Connection) invocation.getArgs()[0];
                PreparedStatement countStatement = connection.prepareStatement(countSql);
                ParameterHandler parameterHandler = (ParameterHandler) metaObject.getValue("delegate.parameterHandler");
                parameterHandler.setParameters(countStatement);
                ResultSet rs = countStatement.executeQuery();
                if (rs.next()) {
                    page.setTotalRows(rs.getInt(1));
                    page.recount();
                }
            }
            String pageSql = sql + " limit " + (page.getNo() - 1) * page.getSize() + "," + page.getSize();
            metaObject.setValue("delegate.boundSql.sql", pageSql);
            LOGGER.debug(pageSql.replaceAll("\n", "").replaceAll(" +", " "));
        } else if (id.matches(".+Count1Page$")) {
            //这里是第一次才计算分页
            BoundSql boundSql = statementHandler.getBoundSql();
            Map<String, Object> params = (Map<String, Object>) boundSql.getParameterObject();
            PageBean page = (PageBean) params.get("page");
            String sql = boundSql.getSql();
            if (page.getNo() == 1) {
                String countSql = "select count(1) from (" + sql + ") as_page_count";
                // String countSql = this.getCountSql(sql);
                Connection connection = (Connection) invocation.getArgs()[0];
                PreparedStatement countStatement = connection.prepareStatement(countSql);
                ParameterHandler parameterHandler = (ParameterHandler) metaObject.getValue("delegate.parameterHandler");
                parameterHandler.setParameters(countStatement);
                ResultSet rs = countStatement.executeQuery();
                if (rs.next()) {
                    page.setTotalRows(rs.getInt(1));
                    page.recount();
                }
            } else {
                page.recount();
            }
            String pageSql = sql + " limit " + (page.getNo() - 1) * page.getSize() + "," + page.getSize();
            metaObject.setValue("delegate.boundSql.sql", pageSql);
            LOGGER.debug(pageSql.replaceAll("\n", "").replaceAll(" +", " "));
        }
        // Connection connection = (Connection) invocation.getArgs()[0];
        // String dbId = mappedStatement.getDatabaseId();
        // System.out.println("=========Connection:"+connection+" dbId:"+dbId);
        return invocation.proceed();
    }

    private String getCountSql(String sql) {
        return " SELECT count(1) " + removeSelect(removeOrders(sql));
    }

    static final Pattern PATTERN_ORDER_BY = Pattern.compile("ORDER\\s*by[\\w|\\W|\\s|\\S]*", Pattern.CASE_INSENSITIVE);

    protected String removeOrders(String sql) {
        int last = sql.lastIndexOf(")");
        StringBuffer sb = new StringBuffer();
        if (last > -1) {
            //有)的情况，从)之后再进行匹配
            sb.append(sql, 0, last);
            sql = sql.substring(last);
        }
        //没有)的，则直接替换，对于有2个order by子句而没有括号的情况，则会直接从第一个后面进行删除
        Matcher m = PATTERN_ORDER_BY.matcher(sql);
        while (m.find()) {
            m.appendReplacement(sb, "");
        }
        m.appendTail(sb);
        return sb.toString();
    }

    // 去除sql语句中select子句
    private static String removeSelect(String hql) {
        int beginPos = hql.toLowerCase().indexOf("from");
        if (beginPos < 0) {
            throw new IllegalArgumentException(" hql : " + hql + " must has a keyword 'from'");
        }
        return hql.substring(beginPos);
    }

    /*
     * 拦截器需要拦截的对象
     */
    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }

    /*
     * 设置初始化的属性值
     */
    @Override
    public void setProperties(Properties properties) {

    }

    public static void main(String[] args) {
        PageInterceptor p = new PageInterceptor();
        String sql = "select * from aaa  order by ac,ae desc   union all select * from ddd where 1=1 and dd =88 order by aaa desc,ggg desc";
        String ret = p.removeOrders(sql);
        System.out.println(ret);
    }
}

